package com.hzlj.position.locate.controller;


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fz.common.base.core.DistanceUtils;
import com.fz.common.base.core.JsonUtils;
import com.fz.common.base.dto.PageParamDTO;
import com.fz.common.base.dto.QueryPageResultDTO;
import com.fz.common.base.dto.ResultDTO;
import com.fz.common.base.exception.PubException;
import com.fz.common.base.office.EasyExcelUtil;
import com.hzlj.position.config.common.dto.common.PositionAddDTO;
import com.hzlj.position.config.common.dto.common.PositionHandleQueryResultDTO;
import com.hzlj.position.config.common.enums.WarningType;
import com.hzlj.position.locate.common.dto.position.*;
import com.hzlj.position.locate.common.dto.positionNewest.PositionNewestQueryParamDTO;
import com.hzlj.position.locate.common.dto.positionNewest.PositionNewestQueryResultDTO;
import com.hzlj.position.locate.common.dto.positionNewest.PositionNewestQuerySimpleResultDTO;
import com.hzlj.position.locate.common.dto.positionNewestEffective.PositionNewestEffectiveQueryParamDTO;
import com.hzlj.position.locate.common.dto.positionNewestEffective.PositionNewestEffectiveQueryResultDTO;
import com.hzlj.position.locate.service.PositionAddTaskService;
import com.hzlj.position.locate.service.PositionNewestEffectiveService;
import com.hzlj.position.locate.service.PositionNewestService;
import com.hzlj.position.locate.service.PositionService;
import com.hzlj.sqjz.config.common.enums.HandleStatus;
import com.hzlj.sqjz.lc.common.dto.lc.LcQueryParamDTO;
import com.hzlj.sqjz.lc.service.LcService;
import com.hzlj.sqjz.system.service.JgService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 【定位记录-分表】接口
 *
 * @author lifh
 * @date 2023-03-28 21:11:50
 */
@Slf4j
@RestController
@RequestMapping("/position/locate")
public class PositionController {
    @Resource
    private PositionService positionService;
    @Resource
    private PositionNewestService positionNewestService;
    @Resource
    private PositionAddTaskService positionAddTaskService;
    @Resource
    private PositionNewestEffectiveService positionNewestEffectiveService;
    @Resource
    private LcService lcService;
    @Resource
    private JgService jgService;

    /**
     * 查询
     */
    @GetMapping(value = "/{id}")
    public ResultDTO<PositionQueryResultDTO> getPosition(@PathVariable("id") String id, Date positionTime) {
        return new ResultDTO<>(
                positionService.getPosition(id, positionTime)
        );
    }

    /**
     * 查询ext
     */
    @GetMapping(value = "/ext/{id}")
    public ResultDTO<PositionExtDTO> getPositionExt(@PathVariable("id") String id, Date positionTime) {
        return new ResultDTO<>(
                positionService.getPositionExt(id, positionTime)
        );
    }

    /**
     * 导出 定位记录-分表
     * fileName：传入的文件名称
     */
    @GetMapping("/export")
    public void export(HttpServletResponse response,
                       String fileName,
                       PositionQueryParamDTO dto) {
        List<PositionQueryResultDTO> list = positionService.listPosition(dto);
        EasyExcelUtil.downCustomerExcel(response,
                fileName,
                fileName,
                jgService.export3Level(PositionQueryResultDTO::getJgCode, PositionQueryResultDTO::getJgMc)
                        .add(PositionQueryResultDTO::getXm, "姓名")
                        .add(PositionQueryResultDTO::getChannelText, "定位方式")
                        .add(PositionQueryResultDTO::getDeviceCode, "定位设备号")
                        .add(PositionQueryResultDTO::getPositionTime, "定位时间")
                        .add(PositionQueryResultDTO::getPosition, "位置信息")
                        .add(PositionQueryResultDTO::getWarningTypeText, "状态")
                        .add(PositionQueryResultDTO::getHandleStatusText, "是否处理")
                        .getFields(),
                list
        );
    }

    /**
     * 列表查询 定位记录-分表
     */
    @GetMapping("/list")
    public ResultDTO<List<PositionQueryResultDTO>> listPosition(PositionQueryParamDTO dto) {
        return new ResultDTO<>(positionService.listPosition(dto));
    }

    /**
     * 分页查询 定位记录-分表
     */
    @GetMapping("/list/page")
    public ResultDTO<QueryPageResultDTO<PositionQueryResultDTO>> pagePosition(PositionQueryParamDTO dto, PageParamDTO pageParamDTO) {
        QueryPageResultDTO<PositionQueryResultDTO> result = positionService.pagePosition(dto, pageParamDTO);
        if (ObjectUtil.isEmpty(result.getList())) {
            return new ResultDTO<>(result);
        }
        List<PositionQueryResultDTO> positions = result.getList();
        for (PositionQueryResultDTO position : positions) {
            if (HandleStatus.DONE.getStatus().equals(position.getHandleStatus())) {
                position.setHandleExt(positionService.getHandleResultExt(position.getId(), position.getPositionTime()));
            }
        }
        return new ResultDTO<>(result);
    }

    /**
     * 导出 定位记录-分表
     * fileName：传入的文件名称
     */
    @GetMapping("/newest/export")
    public void exportNewest(HttpServletResponse response,
                             String fileName,
                             PositionNewestQueryParamDTO dto) {
        List<PositionNewestQueryResultDTO> list = positionNewestService.listPositionNewest(dto);
        EasyExcelUtil.downCustomerExcel(response,
                fileName,
                fileName,
                jgService.export3Level(PositionNewestQueryResultDTO::getJgCode, PositionNewestQueryResultDTO::getJgMc)
                        .add(PositionNewestQueryResultDTO::getXm, "姓名")
                        .add(PositionNewestQueryResultDTO::getChannelText, "定位方式")
                        .add(PositionNewestQueryResultDTO::getDeviceCode, "定位设备号")
                        .add(PositionNewestQueryResultDTO::getPositionTime, "定位时间")
                        .add(PositionNewestQueryResultDTO::getPosition, "位置信息")
                        .add(PositionNewestQueryResultDTO::getWarningTypeText, "状态")
                        .add(PositionNewestQueryResultDTO::getHandleStatusText, "是否处理")
                        .getFields(),
                list
        );
    }

    /**
     * 列表查询 实时定位
     */
    @GetMapping("/newest/list")
    public ResultDTO<List<PositionNewestQueryResultDTO>> pageNewestPosition(PositionNewestQueryParamDTO dto) {
        return new ResultDTO<>(positionNewestService.listPositionNewest(dto));
    }

    /**
     * 分页查询 实时定位
     */
    @GetMapping("/newest/list/page")
    public ResultDTO<QueryPageResultDTO<PositionNewestQueryResultDTO>> pageNewestPosition(PositionNewestQueryParamDTO dto, PageParamDTO pageParamDTO) {
        return new ResultDTO<>(positionNewestService.pagePositionNewest(dto, pageParamDTO));
    }

    /**
     * 列表查询 定位记录-最新有效位置
     */
    @GetMapping("/newest/effective/list")
    public ResultDTO<List<PositionNewestEffectiveQueryResultDTO>> listPositionNewestEffective(PositionNewestEffectiveQueryParamDTO dto){
        log.debug("【查询无分页列表】信息定位记录-最新有效位置,PositionNewestEffectiveQueryParamDTO={}",dto);
        return new ResultDTO<>(positionNewestEffectiveService.listPositionNewestEffective(dto));
    }

    /**
     * 处理异常
     */
    @PostMapping("/handle")
    public ResultDTO<Boolean> handle(@RequestBody PositionHandleDTO dto) {
        log.info("定位异常处理：{}", JsonUtils.obj2json(dto));
        try {
            //更新分表
            this.positionService.handle(dto);
        } catch (Exception e) {
            log.error("定位异常处理失败：{}", JsonUtils.obj2json(dto), e);
            throw new PubException("定位异常处理失败");
        }
        return ResultDTO.ok();
    }


    /**
     * 手动一键同步
     */
    @PostMapping("/sync")
    public ResultDTO<Boolean> handle(@RequestBody PositionQueryParamDTO dto) {
        log.info("手动一键同步：{}", JsonUtils.obj2json(dto));
        try {
            //更新分表
            this.positionService.sync(dto);
        } catch (Exception e) {
            log.error("手动一键同步失败：{}", JsonUtils.obj2json(dto), e);
            throw new PubException("手动一键同步失败");
        }
        return ResultDTO.ok();
    }


    /**
     * 添加定位数据
     */
    @PutMapping("")
    public PositionAddResultDTO add(@RequestBody PositionAddDTO dto) {
        log.info("传入定位数据:dto={}", JsonUtils.obj2json(dto));
        if (ObjectUtil.hasEmpty(dto.getLatitude(), dto.getLongitude(), dto.getPosition())) {
            log.warn("位置上报失败:位置信息为空:deviceCode={}", dto.getDeviceCode());
            return PositionAddResultDTO.fail("位置信息为空");
        }
        return this.positionAddTaskService.add(dto);
    }


    /**
     * 上下线位置上报
     */
    @PostMapping(value = "/online")
    public ResultDTO<Boolean> online(@RequestBody PositionOnlineDTO dto) {
        log.info("【上下线状态处理】dto={}", dto);
        PositionAddDTO position = dto.getPosition();
        if (position == null) {
            if (Boolean.TRUE.equals(dto.getOnline())) {
                throw new PubException("上线请传入位置信息");
            } else {
                position = new PositionAddDTO();
            }
        }
        position.setJzId(dto.getJzId());
        position.setDeviceCode(dto.getDeviceCode());
        position.setChannelType(dto.getChannelType());
        position.setChannel(dto.getChannel());
        position.setWarningType(Boolean.FALSE.equals(dto.getOnline()) ?
                WarningType.POWER_OFF.getCode() : WarningType.SUCCESS.getCode());
        positionAddTaskService.add(position);
        return new ResultDTO<>();
    }


    /**
     * 获取处理状态数据
     */
    @GetMapping(value = "/handleResult/{id}")
    public ResultDTO<PositionHandleQueryResultDTO> getHandleResult(@PathVariable("id") String id, Date positionTime) {
        log.info("【获取处理状态】id={},positionTime={}", id, positionTime);
        return new ResultDTO<>(
                positionService.getHandleResult(id, positionTime)
        );
    }

    /**
     * 距离最近一小时内的定位记录距离判断
     */
    @GetMapping("/far/check/{jzId}")
    private ResultDTO<PositionFarCheckResultDTO> farCheck(@PathVariable("jzId") String jzId,
                                                          String jgCode,
                                                          String longitude,
                                                          String latitude) {
        Date now = new Date();
        PositionNewestQueryResultDTO position = this.positionNewestService.getPositionNewestByJzId(jzId);
        if (position == null) {
            return ResultDTO.ok(new PositionFarCheckResultDTO(false, "没有最新定位记录"));
        }
        //大于1个小时
        if (DateUtil.between(position.getPositionTime(), now, DateUnit.MINUTE, false) > 60) {
            return ResultDTO.ok(new PositionFarCheckResultDTO(false, "最新定位记录在1个小时之外"));
        }
        BigDecimal distance = BigDecimal.valueOf(DistanceUtils.amapDistance(
                new BigDecimal(position.getLongitude()).doubleValue(),
                new BigDecimal(position.getLatitude()).doubleValue(),
                new BigDecimal(longitude).doubleValue(),
                new BigDecimal(latitude).doubleValue())
        );

        BigDecimal distanceKil = distance.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
        boolean far = distance.compareTo(BigDecimal.valueOf(50000)) >= 0;
        return ResultDTO.ok(PositionFarCheckResultDTO.of(position)
                .setDistance(distance)
                .setFar(far)
                .setFarMsg(far ? "距离最近一次定位记录为：" + distanceKil + "千米,请确认定位是否准确!" : "未发现位置异常")
        );
    }


    /**
     * 分页查询 实时定位(附带流程信息)
     */
    @SuppressWarnings("unchecked")
    @GetMapping("/withLc/{lclx}/list/page")
    public ResultDTO<QueryPageResultDTO<?>> pageNewestPositionWithLc(@PathVariable("lclx") String lclx,
                                                                     LcQueryParamDTO dto,
                                                                     PageParamDTO page) {
        QueryPageResultDTO<?> result = lcService.pageLc(lclx, dto, page);
        //转换成map
        List<Map<Object, Object>> listByMap = result.getList().stream().map(e -> (Map<Object, Object>) JsonUtils.obj2map(e)).collect(Collectors.toList());
        this.fillPosition(listByMap);
        return ResultDTO.ok(
                new QueryPageResultDTO<>(result.getTotalPage(), result.getTotalElements(), result.getCurrentPage(), result.getCurrentSize(), result.getLastPage(), listByMap)
        );
    }

    /**
     * 导出 实时定位(附带流程信息)
     */
    @SuppressWarnings("unchecked")
    @GetMapping("/withLc/{lclx}/export")
    public void pageNewestPositionWithLc(HttpServletResponse response,
                                         @PathVariable("lclx") String lclx,
                                         LcQueryParamDTO dto) {
        List<?> list = lcService.listLc(lclx, dto);
        //转换成map
        List<Map<Object, Object>> listByMap = list.stream().map(e -> (Map<Object, Object>) JsonUtils.obj2map(e)).collect(Collectors.toList());
        fillPosition(listByMap);
        String fileName = "外出请假";
        String mdd = "外出地点明细";
        StringBuilder positionStatus = new StringBuilder("越界-已请假");
        switch (lclx) {
            case "wcqj"://外出请假
                break;
            case "ksx"://跨市县
                fileName = "经常跨市县活动";
                mdd = "跨市县目的地明细";
                positionStatus.setLength(0);
                positionStatus.append("越界-跨市县");
                break;
            default:
                throw new PubException("未支持的导出项目");
        }
        EasyExcelUtil.downCustomerExcel(response, fileName, fileName,
                EasyExcelUtil.Field.<Map<Object, Object>>of()
                        .add(e -> ((Map<?, ?>) e.get("jzJbxx")).get("jgMc"), "机构")
                        .add(e -> ((Map<?, ?>) e.get("jzJbxx")).get("xm"), "姓名")
                        .add(e -> ((Map<?, ?>) e.get("jzJbxx")).get("xbText"), "性别")
                        .add(e -> e.get("position") != null ? ((PositionNewestQuerySimpleResultDTO) e.get("position")).getDeviceCode() : "/", "定位设备号")
                        .add(e -> e.get("sqksrq"), "外出日期")
                        .add(e -> e.get("sqjsrq"), "结束日期")
                        .add(e -> e.get("sqts"), "天数")
                        .add(e -> e.get("mdd") + (ObjectUtil.isNotEmpty(e.get("mddXxdz")) ? ("/" + e.get("mddXxdz")) : ""), mdd)
                        .add(e -> e.get("position") != null ? ((PositionNewestQuerySimpleResultDTO) e.get("position")).getPositionTime() : "/", "定位时间")
                        .add(e -> e.get("position") != null ? ((PositionNewestQuerySimpleResultDTO) e.get("position")).getPosition() : "/", "定位信息")
                        .add(e -> e.get("position") != null ? (Boolean.TRUE.equals(((PositionNewestQuerySimpleResultDTO) e.get("position")).getLeaving()) ? positionStatus : "成功") : "/", "状态")
                        .getFields(),
                listByMap
        );
    }

    private void fillPosition(List<Map<Object, Object>> list) {
        if (ObjectUtil.isEmpty(list)) {
            return;
        }
        //JzIds
        List<String> jzIds = list.stream().filter(ObjectUtil::isNotEmpty).map(e -> e.get("jzId").toString()).collect(Collectors.toList());
        //对应的最新定位数据
        List<PositionNewestQuerySimpleResultDTO> positions = positionNewestService.getPositionNewestByJzIds(jzIds);
        Map<String, PositionNewestQuerySimpleResultDTO> positionByJzId = positions.stream().collect(Collectors.toMap(PositionNewestQuerySimpleResultDTO::getJzId, e -> e));
        for (Map<Object, Object> map : list) {
            Object jzId = map.get("jzId");
            if (jzId != null && positionByJzId.get(jzId.toString()) != null) {
                map.put("position", positionByJzId.get(jzId.toString()));
            }
        }
    }
}

